package com.wxapp.common.util.condition;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wxapp.common.constant.base.IBaseRequestPage;
import com.wxapp.common.util.check.CheckUtil;
import com.wxapp.common.util.copy.CopyUtil;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.lang.Nullable;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Supplier;

/**
 * @author lgl
 */
public class ConditionUtil {
    private final static List<SqlKeyword> ignoreSql = List.of(SqlKeyword.DESC, SqlKeyword.ASC, SqlKeyword.GROUP_BY);

    private ConditionUtil() {
    }

    /**
     * 获取page
     */
    public static <T, S extends IBaseRequestPage> Page<T> getPage(S vm, Class<T> cl) {
        var page = new Page<T>(vm.getPage(), vm.getSize());
        page.setOptimizeCountSql(false);
        page.setOrders(vm.getOrder());
        return page;
    }

    /**
     *  查询统一工具类
     *
     * @Param: supplier 返回值类型
     * @Param: fClass 前端参数
     * @Param: result 返回值字段
     * @Param: condition 特定查询条件
     * @Param: ignoreProperties 忽略条件
     * @Author: lgl
     * @Date 2023/5/23 8:54
     */
    @SafeVarargs
    public static <T, S> LambdaQueryWrapper<T> getSCondition(Supplier<T> source,
                                                                                 Object value,
                                                                                 @Nullable Class<S> result,
                                                                                 @Nullable Map<SFunction<T, ?>, SqlKeyword> condition,
                                                                                 @Nullable String userId,
                                                                                 @Nullable SFunction<T, ?>... ignoreProperties) {
        try {
            Class<?> aClass = source.get().getClass();
            QueryWrapper<T> query = Wrappers.query();
            Map<String, SqlKeyword> condit = new HashMap<>();
            Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(aClass);
            List<String> ignoreList = new ArrayList<>();
            if (condition != null && !condition.isEmpty()) {
                condition.forEach((k, v) -> {
                    var key = columnMap.get(LambdaUtils.formatKey(PropertyNamer.methodToProperty(LambdaUtils.extract(k).getImplMethodName()))).getColumn();
                    condit.put(key, v);
                    if (!ignoreSql.contains(v)) {
                        ignoreList.add(key);
                    }
                });
            }
            Map<String, Object> map = new HashMap<>();
            if (ignoreProperties != null && ignoreProperties.length > 0) {
                var ignore = new ArrayList<>(Arrays.asList(ignoreProperties));
                ignore.forEach(s -> {
                    if (s != null) {
                        ignoreList.add(columnMap.get(LambdaUtils.formatKey(PropertyNamer.methodToProperty(LambdaUtils.extract(s).getImplMethodName()))).getColumn());
                    }
                });
            }
            var aNew = CopyUtil.copyObjectNew(value, source, null);
            Field[] resultFields = null;
            if (result != null) {
                resultFields = result.getDeclaredFields();
            }
            List<String> resultNames = new ArrayList<>();
            for (Field field : aClass.getDeclaredFields()) {
                field.setAccessible(true);
                var name = StringUtils.camelToUnderline(field.getName());
                var fileValue = field.get(aNew);
                if (CheckUtil.isNullObject(fileValue)) {
                    continue;
                }
                if (condit.get(name) != null) {
                    dealCondition(query, name, condit.get(name), fileValue);
                    continue;
                }
                if (CheckUtil.isNotNullList(ignoreList) && ignoreList.contains(name)) {
                    continue;
                }
                if (CheckUtil.isNotNullObject(fileValue)) {
                    map.put(name, fileValue);
                }
                if (resultFields != null && Arrays.stream(resultFields).anyMatch(s -> StringUtils.camelToUnderline(s.getName()).equals(name))) {
                    resultNames.add(name);
                }
            }
            if (!resultNames.isEmpty()) {
                query.select(resultNames.toArray(new String[0]));
            }
            query.eq("status", true)
                    .eq(CheckUtil.isNotNullString(userId),"create_by", userId)
                    .orderByDesc("create_time");
            if (map.isEmpty()) {
                return query.lambda();
            }
            return query.allEq(map).lambda();
        } catch (Exception e) {
            throw new RuntimeException("条件查询出错");
        }
    }

    private static <T> void dealCondition(QueryWrapper<T> queryWrapper, String key, SqlKeyword sqlKeyword, Object value) {
        switch (sqlKeyword) {
            case LIKE->queryWrapper.like(key, value);
            case NOT_LIKE->queryWrapper.notLike(key, value);
            case DESC->queryWrapper.orderByDesc(key);
            case ASC->queryWrapper.orderByAsc(key);
            case EQ->queryWrapper.eq(key, value);
            case NE->queryWrapper.ne(key, value);
            case GE->queryWrapper.ge(key, value);
            case GT->queryWrapper.gt(key, value);
            case LE->queryWrapper.le(key, value);
            case LT->queryWrapper.lt(key, value);
            case IS_NOT_NULL->queryWrapper.isNotNull(key);
            case IS_NULL->queryWrapper.isNull(key);
            case GROUP_BY->queryWrapper.groupBy(key);
        }
    }
}
